package org.stars.util.reflect.test1;

import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.sql.Timestamp;
import static java.lang.Math.*;

/**
 *  Classe rappresentante un periodo temporale ordinato (dal > al)
 *
 * Insiel s.p.a.
 * A. Svetina (908303) 29-gen-2007
 */
public class TimeSpan
{

    /**
     * verion id per la serializzazione
     */
    private static final long serialVersionUID = 2811353785912739314L;
    /**
     * Estremo inferiore di default :  01/01/1900
     */
    public static final Timestamp MIN_DATE = new Timestamp(-2208992400000L);
    /**
     * Estremo superiore di default : 31/12/2999
     */
    public static final Timestamp MAX_DATE = new Timestamp(32503590000000L);
    /**
     * Numero di millisecondi presenti in 1 giorno
     */
    private static final long MILLISEC_X_DAY = 1000*60*60*24;
    /**
     * Stringa di formato standard per la data accettata nel costruttore
     */
    private static final SimpleDateFormat DT_FMT = new SimpleDateFormat("dd/MM/yyyy");

    /**
     * Costruisce un oggetto TimeSpan a partire da due date (da, a) espresse in stringa in formato dd/MM/yyyy
     * se uno degli estremi o entrambi sono null vengono presi i default : 01/01/1900, 31/12/2999
     * @param dal data di inizio
     * @param al data  di fine
     * @throws Exception se formato input non valido oppure intervallo invertito
     */
    public TimeSpan(String dal, String al) throws Exception
    {
        this();
        if (dal != null) setDal(DT_FMT.parse(dal));
        if (al != null) setAl(DT_FMT.parse(al));
    }

    /**
     * Costruisce un oggetto TimeSpan a partire da una date (da) espresse in stringa in formato dd/MM/yyyy
     * l'estremo superiore viene considerato al valore di default : 31/12/2999
     * @param dal data di inizio
     * @throws Exception se formato input non valido oppure intervallo invertito
     */
    public TimeSpan(String dal) throws Exception
    {
        this(dal,null);
    }

    /**
     * Costruisce un oggetto TimeSpan a partire da due date (da, a)
     * se uno degli estremi o entrambi sono null vengono presi i default : 01/01/1900, 31/12/2999
     * @param dal data di inizio
     * @param al data  di fine
     */
    public TimeSpan(Date dal, Date al)
    {
        this();
        setDal(dal);
        setAl(al);
    }

    /**
     * Costruisce un oggetto TimeSpan a partire da una date (da)
     * l'estremo superiore viene considerato al valore di default : 31/12/2999
     * @param dal data di inizio
     */
    public TimeSpan(Date dal)
    {
        this(dal,null);
    }

    /**
     * Costruisce un oggetto TimeSpan a partire da un altro TimeSpan
     * @param ts timespan da cui crearne il nuovo
     */
    public TimeSpan(TimeSpan ts)
    {
        this();
        if (ts != null)
        {
            setDal(ts.getDal());
            setAl(ts.getAl());
        }
    }

    /**
     * Costruisce un Timespan impostato al periodo di default 01/01/1900, 31/12/2999
     */
    public TimeSpan()
    {
        init();
    }

    /**
     * Ritorna l'estremo superiore del periodo
     * @return timestamp estremo superiore
     */
    public Timestamp getAl()
    {
        return this.al;
    }

    /**
     * Modifica l'estremo superiore del periodo
     * @param al data da settare come estremo sup
     */
    public void setAl(Date al)
    {
        setAl((al == null)?null:new Timestamp(al.getTime()));
    }

    /**
     * Modifica l'estremo superiore del periodo
     * Se l'estremo passato e' null prende l'estremo di default : 31/12/2999
     * Effettua il controllo se l'estremo sup sia inferiore all'estremo inf in caso lancia un eccezione
     * @param al timestamp da settare come estremo sup
     */
    public void setAl(Timestamp al)
    {
        if (al == null) al = MAX_DATE;

        if (al.compareTo(dal) < 0)
            throw  new IllegalArgumentException("Intervallo non valido!" +
        			"\nDal -> " + dal + "\nAl -> " + al);

        this.al = al;
        this.domenicheDaCalcolare = true;
    }


    /**
     * Ritorna l'estremo inferiore del periodo
     * @return timestamp estremo inferiore
     */
    public Timestamp getDal()
    {
        return this.dal;
    }

    /**
     * Modifica l'estremo inferiore del periodo
     * @param dal data da settare come estremo inf
     */
    public void setDal(Date dal)
    {
        setDal((dal == null)?null:new Timestamp(dal.getTime()));
    }

    /**
     * Modifica l'estremo inferiore del periodo
     * Se l'estremo passato e' null prende l'estremo di default : 01/01/1900
     * Effettua il controllo se l'estremo sup sia inferiore all'estremo inf in caso lancia un eccezione
     * @param dal timestamp da settare come estremo inf
     */
    public void setDal(Timestamp dal)
    {
        if (dal == null) dal = MIN_DATE;

        if (dal.compareTo(al) > 0)
            throw  new IllegalArgumentException("Intervallo non valido!" +
            			"\nDal -> " + dal + "\nAl -> " + al);

        this.dal = dal;
        this.domenicheDaCalcolare = true;
    }

    /**
     * Determina se oggetto passato e' uguakle al TimeSpan  (testa ugualianza delle coppie di estremi)
     * @param o oggetto TimeSpan
     * @return true se sono uguali, false in caso contrario falso
     */
    @Override
	public boolean equals(Object o)
    {
        if (this == o)
        {
            return true;
        }
        if (o == null || getClass() != o.getClass())
        {
            return false;
        }

        TimeSpan ts = (TimeSpan) o;

        return this.al.equals(ts.al) && this.dal.equals(ts.dal);

    }

    /**
     * Controlla se il TimeSpan passato contiene o coincidente con il dal presente <code>TimeSpan</code>
     * @param ts oggetto TimeSpan
     * @return true se sono uguali, false in caso contrario falso
     */
    public boolean isIncluded(TimeSpan ts)
    {

        return ts != null && !(this.dal.before(ts.dal)) && !(this.al.after(ts.al));

    }

    @Override
	public int hashCode()
    {
        return 31 * dal.hashCode() + al.hashCode();
    }

    /**
     * Controlla se il TimeSpan passato e' contenuto o coincidente con il dal presente <code>TimeSpan</code>
     * @param ts TimeSpan da controllare
     * @return true il TimeSpan passato e' contenuto o coincidente con il dal presente <code>TimeSpan</code>,
     * false in caso contrario
    */
    public boolean isOverall(TimeSpan ts)
    {
        return ts != null && (this.dal.equals(ts.getDal()) || this.dal.before(ts.getDal()))
                && (this.al.equals(ts.getAl()) || this.al.after(ts.getAl()));
    }

    /**
     * Controlla se il Timestamp passato e' contenuto o coincidente con gli estremi il dal presente <code>TimeSpan</code>
     * @param ts TimeStamp da controllare
     * @return true il TimeSpan passato e' contenuto o coincidente con il dal presente <code>TimeSpan</code>,
     * false in caso contrario
    */
    public boolean isOverall(Timestamp ts)
    {
        return ts != null && (this.dal.equals(ts) || this.dal.before(ts))
                && (this.al.equals(ts) || this.al.after(ts));
    }

    /**
     * Controlla se il TimeSpan passato e' intersecato  con il dal presente <code>TimeSpan</code>
     * @param ts TimeSpan da controllare
     * @return true il TimeSpan passato e' intersecato  con il dal presente <code>TimeSpan</code>,
     * false in caso contrario
    */
    public boolean  isOverlapped(TimeSpan ts)
    {
        return ts != null && this.compareTo(ts) == 0;
    }

    /**
     * Restituisce la durata in giorni, arrotondata per difetto, del periodo descritto dal presente <code>TimeStamp</code>
     * @return		la durata in giorni dell'istanza
     */
    public long getLength()
    {
    	long gapInMillisec = this.al.getTime() - this.dal.getTime() + MILLISEC_X_DAY; 
    	return (long) floor(gapInMillisec / MILLISEC_X_DAY) ;
    }

    /**
     * Crea una lista di TimeSpan creati dall'inserimento del periodo passato come argomento nel periodo rappresentato
     * dal presente <code>TimeSpan</code>
     * Secondo le seguenti modalita:
     *
     * 1) I periodi non si sovrappongono torna la lista vuota.
     * 2) I periodi sono coincidenti torna il periodo stesso.
     * 3) Il periodo e' contenuto completamente nel presente <code>TimeSpan</code> (senza sovrapposizione degli estremi)
     *    torna una lista di 3 periodi cosi' stutturata {this.dal, ts.dal-1}; {ts.dal, ts.al}; {ts.al+1, this.al}
     * 4) il periodo interseca il presente <code>TimeSpan</code> con i 2 estremi superiori sovrapponsti
     *    torna una lista di 2 periodi cosi' stutturata {this.dal, ts.dal-1}; {ts.dal, this.al}
     * 5) il periodo interseca il presente <code>TimeSpan</code> con i 2 estremi inferiori sovrapponsti
     *    torna una lista di 2 periodi cosi' stutturata {this.dal, ts.al}; {ts.al+1, this.al}
     *
     * @param ts TimeSpan che potrebbe generare la spezzatura dal presente <code>TimeSpan</code>,
     * @return Lista di TimeSpan rappresentanti le spezzature, lista vuota se i periodi non si intersecano
     */
    public List<TimeSpan>split(TimeSpan ts)
    {
        List<TimeSpan> tss = new ArrayList<TimeSpan>();

        if (this.isOverlapped(ts))                  // 1) I 2 periodi sono sovrapposti (this ? ts) ? �
            if (this.equals(ts))                    // 2) I 2 periodi sono coincidenti (this ? ts)
                tss.add(ts);                                        // crea un periodo con estremi {ts.dal, ts.al}
            else if (this.isOverall(ts) &&          // 3) un periodo contiene completamente l'altro (this C ts)
                     this.boundTo(ts) == 0)
            {
                tss.add(new TimeSpan(dal,dayBefore(ts.getDal())));  // crea un periodo con estremi {this.dal, ts.dal-1}
                tss.add(ts);                                        // crea un periodo con estremi {ts.dal, ts.al}
                tss.add(new TimeSpan(dayAfter(ts.getAl()),al));     // crea un periodo con estremi {ts.al+1, this.al}
            }
            else if (this.isOverall(ts.getDal()) && // 4) il primo estremo appartiene al periodo (dal � this)
                     this.boundTo(ts) != -1)
            {
                tss.add(new TimeSpan(dal,dayBefore(ts.getDal())));  // crea un periodo con estremi {this.dal, ts.dal-1}
                tss.add(new TimeSpan(ts.getDal(),al));              // crea un periodo con estremi {ts.dal, this.al}
            }
            else if (this.isOverall(ts.getAl()) &&  // 5) il secondo estremo appartiene al periodo (al � this)
                     this.boundTo(ts) != 1)
            {
                tss.add(new TimeSpan(dal,ts.getAl()));              // crea un periodo con estremi {this.dal, ts.al}
                tss.add(new TimeSpan(dayAfter(ts.getAl()),al));     // crea un periodo con estremi {ts.al+1, this.al}
            }

        return tss;
    }

    /**
     * Incrementa il TimeStamp ,passato di 1 gg ritornando un nuovo TimeStamp
     * @param ts Timestamp da incrementare di 1 gg
     * @return TimeStamp risultante
     */
    public static Timestamp dayBefore(Timestamp ts)
    {
    	Timestamp dayBefore = new Timestamp(ts.getTime());
        dayBefore.setTime(ts.getTime()-MILLISEC_X_DAY);
        return dayBefore;
    }
    /**
     * Decrementa il TimeStamp passato di 1 gg ritornando un nuovo TimeStamp
     * @param ts Timestamp da decrementare di 1 gg
     * @return TimeStamp risultante
     */
    public static Timestamp dayAfter(Timestamp ts)
    {
    	Timestamp dayAfter = new Timestamp(ts.getTime());
        dayAfter.setTime(ts.getTime()+MILLISEC_X_DAY);
        return dayAfter;
    }

    /**
     * Confronta il periodo contenuto nel presente <code>TimeSpan</code> con quello passato
     * @param ts Timespan da confrontare
     * @return  -1 se <i>this</i> e' antecedente a ts,
     *          +1 se <i>this</i> e' seguente ts
     *          altrimenti 0 (periodi uguali o intersecati)
     */
    public int compareTo(TimeSpan ts)
    {
        return this.al.before(ts.getDal()) ? -1 : this.dal.after(ts.getAl()) ? 1 : 0;
    }

    /**
     * Confronta il periodo contenuto nel presente <code>TimeSpan</code> con quello passato
     * @param ts TimeSpan da confrontare
     * @return  -1 Se ts ha l'estremo inferiore coincidente con l'estremo inferiore di <i>this</i>,
     *          +1 Se ts ha l'estremo superiore coincidente con l'estremo superiore di <i>this</i>,
     *          altrimenti 0 in tutti gli altri casi
     */
    public int boundTo(TimeSpan ts)
    {
        return this.dal.equals(ts.getDal()) ? -1 : this.al.equals(ts.getAl()) ? 1 : 0;
    }

    /**
     * Estremo inferiore periodo
     */
    private Timestamp dal = MIN_DATE;
    /**
     * Estremo superiore periodo
     */
    private Timestamp al = MAX_DATE;

    /**
     * Flag che indica se bisogna ricalcolare o meno il n. di domeniche presenti nel periodo.
     * il flag viene settato a true ad ogni modifica degli estremi del periodo stesso.
     */
    private boolean domenicheDaCalcolare;
    /**
     *  Totale di domeniche presenti nel periodo
     */
    private long domeniche;

    /**
     * Inizializza l'oggetto, settando gli estremi ai valori di default
     */
    public void init()
    {
        setDal(MIN_DATE);
        setAl(MAX_DATE);
        this.domeniche = 0;
    }

    /**
     * Resetta l'oggetto in realta' lo reinizializza (vedi <code>init</code>)
     */
    public void cleanup()
    {
        init();
    }

    /**
     * Versione del <code>toString()</code> per output su HTML
     * @return stringa rappresentante il contenuto (vedi <code>toString()</code>)
     */
    public String toHTML()
    {
        return this.toString();
    }

    /**
     * Ritorna in formato stringa il periodo visualizzando i 2 estremi nel seguente formato:
     * dal,al dove dal e al sono date in formato : dd/MM/yyyy
     */
    @Override
    public String toString()
    {
//    	return "{".concat(DT_FMT.format(this.dal)).concat(",").concat(DT_FMT.format(this.al)).concat("}");
    	return (DT_FMT.format(this.dal)).concat(",").concat(DT_FMT.format(this.al));
    }

    /**
     * Ritorna il numero di domeniche presenti nel periodo, il calcolo viene eseguito solo se
     * non precedentemente gia calcolato (vedere flag <code>domenicheDaCalcolare</code>)
     * @return numero di domeniche presenti nel periodo
     */
    public long getDomeniche()
    {
        if (this.domenicheDaCalcolare)
        {
            this.domeniche = contaDomeniche();
            this.domenicheDaCalcolare = false;
        }
        return this.domeniche;
    }

    /**
     * Restituisce il giorni del  periodo al netto delle domeniche
     * @return numero di ggiorni del periodo - il numero di domeniche
     */
    public long getGiorni26()
    {
        return getLength() - getDomeniche();
    }
    /**
     * Cicla dall'estremo inferiore all'estremo superiore del periodo (inclusi) a passi di 1 gg
     * totalizzando il numero di domeniche presenti
     * @return numero di domeniche
     */
    private long contaDomeniche()
    {
        long count = 0;
        Calendar cal = Calendar.getInstance();
        cal.setTime(this.dal);

        while (!cal.getTime().after(this.al))
        {
            if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) count++;
            cal.add(Calendar.DAY_OF_MONTH,1);
        }

        return count;
    }
    
    /**
     * Spezza il periodo passato come parametro in tanti sottoperiodi suddividendoli per mese.
     * Ogni nuovo periodo generato partir� dal primo giorno del mese cui si riferisce fino
     * e si protrarr� sino all'ultimo giorno dello stesso mese.<br/>
     * Es. un <code>TimeSpan</code> che vada dal 12/12/2006	 al 15/02/2007 produrr� i tre seguenti nuovi periodi:
     * <ul>
     * <li>01/12/2006 - 31/12/2007</li>
     * <li>01/01/2007 - 31/01/2007</li>
     * <li>01/02/2007 - 28/02/2007</li>
     * </ul> 
     * @param periodo il periodo da spezzare
     * @return	la lista dei mesi coperti dal periodo da spezzare
     */
    public static List<TimeSpan> splitMensilita(TimeSpan periodo)
    {
    	List<TimeSpan> periodi = new ArrayList<TimeSpan>();
    	
    	
        Calendar cal = Calendar.getInstance();
        cal.setTime(periodo.getDal());

        do
        {
        	periodi.add( new TimeSpan(firstDayOfMonth(cal.getTime()), lastDayOfMonth(cal.getTime())) );
        	cal.add(Calendar.MONTH,1);
        } while (!cal.getTime().after(periodo.getAl()));
               
        return periodi;
    	
    }
    
    /**
     * Spezza l'istanza di periodo in tanti sottoperiodi suddividendoli per mese.
     * Ogni nuovo periodo generato partir� dal primo giorno del mese cui si riferisce fino
     * e si protrarr� sino all'ultimo giorno dello stesso mese.<br/>
     * Es. un <code>TimeSpan</code> che vada dal 12/12/2006	 al 15/02/2007 produrr� i tre seguenti nuovi periodi:
     * <ul>
     * <li>01/12/2006 - 31/12/2007</li>
     * <li>01/01/2007 - 31/01/2007</li>
     * <li>01/02/2007 - 28/02/2007</li>
     * </ul> 
     * @return	la lista dei mesi coperti dal periodo da spezzare
     */
    public List<TimeSpan> splitMensilita()
    {
    	return splitMensilita(this);
    }

    /**
     * Ritorna un nuovo TimeSpan che ha un dal corrispondente al 1 gg del mese antecedente al Dal
     * del TimeSpan corrente e che ha un al corrispondente all'ultimo gg del mese seguente al Al
     * del TimeSpan corrente.
     *
     * Es.  con un TimeSpan: 14/02/2007,25/02/2007 si otterra': 01/02/2007,28/02/2007
     *      con un TimeSpan: 15/12/2006,13/01/2007 si otterra': 01/12/2006,31/01/2007
     *
     * @return nuovo TimeSpan
     */
    public TimeSpan arrotondaAlMese()
    {
        return new TimeSpan(firstDayOfMonth(this.getDal()),lastDayOfMonth(this.getAl()));
    }

    /**
     * Restringe un periodo temporale ai limiti indicati
     * @param limite		un timespan che setta i due limiti temporali permessi
     * @return				un nuovo Timespan che mantiene le date dell'istanza se
     * 						rientrano entro i limiti temporali indicati, le setta a quelle 
     * 						indicate come limite in caso contrario
     */
    public TimeSpan normalizza(TimeSpan limite)
    {
   		return new TimeSpan(this.getDal().before(limite.getDal()) ? limite.getDal() : this.getDal(),
   			                this.getAl().after(limite.getAl())    ? limite.getAl()  : this.getAl());
    }
    
    /**
     * Restringe un periodo temporale ai limiti indicati
     * @param dal			data minima
     * @param al			data massima
     * @return				un nuovo Timespan che mantiene le date dell'istanza se
     * 						rientrano entro i limiti temporali indicati, le setta a quelle 
     * 						indicate come limite in caso contrario
     */
    public TimeSpan normalizza(Date dal, Date al)
    {
   		return this.normalizza( new TimeSpan(dal, al) );
    }

    
	/**
	 * <p>Estrae un Timestamp pari al 1 gennaio dell'anno del parametro <i>valido</i> passato</p>
	 * 
	 * @param valido il DAL/AL da cui estrarre l'anno
	 * @return l'anno individuato
	 */	
	public static Timestamp getInizioAnno(TimeSpan valido)
	{
		Calendar cal = Calendar.getInstance();
		cal.setTime(valido.getDal());
		cal.set(Calendar.MONTH, Calendar.JANUARY);
		cal.set(Calendar.DAY_OF_MONTH, 1);

		return new Timestamp(cal.getTime().getTime());
	}
	
    
	/**
	 * <p>Estrae un Timestamp pari al 1 gennaio dell'anno del parametro <i>valido</i> passato</p>
	 * 
	 * @param valido il DAL/AL da cui estrarre l'anno
	 * @return l'anno individuato
	 */
	public static Timestamp getFineAnno(TimeSpan valido)
	{
		Calendar cal = Calendar.getInstance();
		cal.setTime(valido.getAl());
		cal.set(Calendar.MONTH, Calendar.DECEMBER);
		cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));

		return new Timestamp(cal.getTime().getTime());
	}
    
	
	/**
	 * <p>Estrae un periodo che va dal 1 gennaio al 31 dicembre dell'anno del parametro <i>valido</i> passato</p>
	 * 
	 * @param valido il DAL/AL da cui estrarre l'anno
	 * @return l'anno individuato
	 */
	public static TimeSpan getAnno(TimeSpan valido)
	{
		// assumendo che il periodo passato come argomento non sia compreso in più di un anno
		
		// ne faccio lo stretch al primo giorno dell'anno in cui si trova...
		Calendar cal = Calendar.getInstance();
		cal.setTime(valido.getDal());
		cal.set(Calendar.MONTH, Calendar.JANUARY);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		Date dal = cal.getTime();
		
		// ...e all'ultimo giorno dell'anno in cui si trova
		cal.setTime(valido.getAl());
		cal.set(Calendar.MONTH, Calendar.DECEMBER);
		cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
		Date al = cal.getTime();
		
		return new TimeSpan(dal, al);
	}

	
	/**
     * Individua il primo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */
    public static Timestamp firstDayOfMonth(Date day)
    {
    	return firstDayOfMonth(new Timestamp(day.getTime()));
    }
    
    /**
     * Individua l' ultimo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */    
    public static Timestamp lastDayOfMonth(Date day)
    {
    	return lastDayOfMonth(new Timestamp(day.getTime()));
    }

    /**
     * Individua il primo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */
    public static Timestamp firstDayOfMonth(Timestamp day)
    {
    	/*
        Calendar cal = Calendar.getInstance();
        cal.setTime(day);
        
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
        
        return new Timestamp(cal.getTime().getTime());
        */
    	return firstDayOfMonth(day, 0);
    }
    
    /**
     * Individua l' ultimo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */       
    public static Timestamp lastDayOfMonth(Timestamp day)
    {
    	/*
        Calendar cal = Calendar.getInstance();
        cal.setTime(day);

        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        
        return new Timestamp(cal.getTime().getTime());
        */
    	return lastDayOfMonth(day, 0);
    }
    
    
    /**
     * Individua l' ultimo giorno del mese precedente a quello di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il' ultimo giorno del mese precedente di quello a cui appartiene il parametro
     */       
    public static Timestamp lastDayOfPreviousMonth(Timestamp day)
    {
    	/*
        Calendar cal = Calendar.getInstance();
        cal.setTime(day);
        cal.add(Calendar.MONTH, -1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        
        return new Timestamp(cal.getTime().getTime());
        */
    	return lastDayOfMonth(day, -1);
    }
    
    /**
     * Individua l' ultimo giorno del mese precedente a quello di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il' ultimo giorno del mese precedente di quello a cui appartiene il parametro
     */    
    public static Timestamp lastDayOfPreviousMonth(Date day)
    {
    	return lastDayOfPreviousMonth(new Timestamp(day.getTime()));
    }
    
    
    
    /**
     * Individua il primo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */
    public static Timestamp firstDayOfPreviousMonth(Timestamp day)
    {
        return firstDayOfMonth(day, -1);
    }
    
    
    /**
     * Individua il primo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */
    public static Timestamp firstDayOfPreviousMonth(Date day)
    {
        return firstDayOfPreviousMonth(new Timestamp(day.getTime()));
    }
    
    
    /**
     * Individua l' ultimo giorno del mese successivo a quello di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il' ultimo giorno del mese successivo a quello a cui appartiene il parametro
     */       
    public static Timestamp lastDayOfNextMonth(Timestamp day)
    {
    	/*
        Calendar cal = Calendar.getInstance();
        cal.setTime(day);
        cal.add(Calendar.MONTH, -1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        
        return new Timestamp(cal.getTime().getTime());
        */
    	return lastDayOfMonth(day, 1);
    }
    
    /**
     * Individua l' ultimo giorno del mese successivo a quello di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il' ultimo giorno del mese successivo a quello a cui appartiene il parametro
     */    
    public static Timestamp lastDayOfNextMonth(Date day)
    {
    	return lastDayOfNextMonth(new Timestamp(day.getTime()));
    }
    
    
    
    /**
     * Individua il primo giorno del mese successivo a quello di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese successivo a quello cui appartiene il parametro
     */
    public static Timestamp firstDayOfNextMonth(Timestamp day)
    {
        return firstDayOfMonth(day, 1);
    }
    
    
    /**
     * Individua il primo giorno del mese successivo a quello di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese successivo a quello cui appartiene il parametro
     */
    public static Timestamp firstDayOfNextMonth(Date day)
    {
        return firstDayOfNextMonth(new Timestamp(day.getTime()));
    }
    
    
    
    /**
     * Individua l' ultimo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */       
    private static Timestamp lastDayOfMonth(Timestamp day, int amount)
    {
        Calendar cal = Calendar.getInstance();
        cal.setTime(day);
        cal.add(Calendar.MONTH, amount);

        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        
        return new Timestamp(cal.getTime().getTime());
    }
    
    
    /**
     * Individua il primo giorno del mese di cui fa parte il giorno passato come parametro
     * @param day		il giorno in esame
     * @return			il primo giorno del mese cui appartiene il parametro
     */
    private static Timestamp firstDayOfMonth(Timestamp day, int amount)
    {
        Calendar cal = Calendar.getInstance();
        cal.setTime(day);
        cal.add(Calendar.MONTH, amount);
        
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
        
        return new Timestamp(cal.getTime().getTime());
    }

    
    /**
     * <p>Restituisce il mese che contiene la data passata come parametro</p>
     * 
     * @param data la data di riferimento
     * @return il mese che contiene la data passata come parametro
     */
    public static TimeSpan getMonth(Timestamp data)
    {
    	Date dal = firstDayOfMonth(data);
    	Date al = lastDayOfMonth(data);
    	
    	return new TimeSpan(dal, al);    	
    }
    
    /**
     * <p>Restituisce il mese che contiene la data passata come parametro</p>
     * 
     * @param data la data di riferimento
     * @return il mese che contiene la data passata come parametro
     */
    public static TimeSpan getMonth(Date data)
    {
    	return getMonth(new Timestamp(data.getTime()));
    }
    
  
	/**
	 * <p>ASSERT: ognuno dei due periodi passati è completamente compreso in un unico anno!!!</p>
	 * <p>stabilisce se due periodi si riferiscono ad uno stesso anno</p>
	 * @param periodo1 primo periodo da considerare
	 * @param periodo2 secondo periodo da considerare
	 * @return true se uguale 
	 */
	public static boolean isSameYear(TimeSpan periodo1, TimeSpan periodo2)
	{
		Calendar cal = Calendar.getInstance();
		Calendar cal2 = Calendar.getInstance();
		cal.setTime(periodo1.getDal());
		cal2.setTime(periodo2.getDal());
		long anno1 = cal.get(Calendar.YEAR);
		long anno2 = cal2.get(Calendar.YEAR);
		
		return anno1 == anno2;
	}
	
	
}
